package twoD.IsoPfem.utils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import twoD.IsoPfem.FlatFace;
import twoD.IsoPfem.IElement;
import twoD.IsoPfem.Mesh;
import twoD.IsoPfem.Node;
import twoD.IsoPfem.Structure;

/**
 * todo: reimplement MeshUtilities with generic type to use for various type of
 * element
 * 
 * @author WINDOWS
 * 
 */
public class MeshUtilities {
	public enum DiagType {
		DIAG_TYPE_FORWARD, DIAG_TYPE_BACKWARD
	};

	public static Node[][] createRectangularMesh(double x0, double y0,
			double dx, double dy, double Lx, double Ly, Mesh mesh,
			DiagType diagType) {
		double[] bx;
		int ny;
		double adx = Math.abs(dx);
		if (Lx - Math.floor(Lx / adx) * adx >= adx / 2)
			ny = (int) (Math.floor(Lx / adx) + 2);
		else
			ny = (int) Math.floor(Lx / adx) + 1;
		bx = new double[ny];
		for (int i = 0; i < ny - 1; i++)
			bx[i] = x0 + i * dx;
		bx[ny - 1] = x0 + (dx / adx) * Lx;

		double[] by;
		int nx;
		double ady = Math.abs(dy);
		if (Ly - Math.floor(Ly / ady) * ady >= ady / 2)
			nx = (int) (Math.floor(Ly / ady) + 2);
		else
			nx = (int) Math.floor(Ly / ady) + 1;
		by = new double[nx];
		for (int i = 0; i < nx - 1; i++)
			by[i] = y0 + i * dy;
		by[nx - 1] = y0 + (dy / ady) * Ly;

		return createRegularMesh(bx, by, mesh, diagType);
	}

	public static Node[][] createRectangularMesh(Node[] base, double dx,
			double dy, double Lx, double Ly, Mesh mesh, DiagType diagType) {
		double x0 = base[0].getPosition().getX1();
		double y0 = base[0].getPosition().getX2();
		double[] bx;
		int ny;
		double adx = Math.abs(dx);
		if (Lx - Math.floor(Lx / adx) * adx >= adx / 2)
			ny = (int) (Math.floor(Lx / adx) + 2);
		else
			ny = (int) Math.floor(Lx / adx) + 1;
		bx = new double[ny];
		for (int i = 0; i < ny - 1; i++)
			bx[i] = x0 + i * dx;
		bx[ny - 1] = x0 + (dx / adx) * Lx;

		double[] by;
		int nx;
		double ady = Math.abs(dy);
		if (Ly - Math.floor(Ly / ady) * ady >= ady / 2)
			nx = (int) (Math.floor(Ly / ady) + 2);
		else
			nx = (int) Math.floor(Ly / ady) + 1;
		by = new double[nx];
		for (int i = 0; i < nx - 1; i++)
			by[i] = y0 + i * dy;
		by[nx - 1] = y0 + (dy / ady) * Ly;

		return createRegularMesh(base, bx, by, mesh, diagType);
	}

	private static Node[][] createRegularMesh(double[] gx, double[] gy,
			Mesh mesh, DiagType diagType) {
		int ny = gx.length;
		int nx = gy.length;
		Node[][] N = new Node[nx][ny];
		for (int i = 0; i < ny; i++)
			for (int j = 0; j < nx; j++)
				N[j][i] = mesh.addNode(new Node(gx[i], gy[j]));
		if (diagType == DiagType.DIAG_TYPE_FORWARD)
			for (int i = 0; i < ny - 1; i++)
				for (int j = 0; j < nx - 1; j++) {
					mesh.addFace(new FlatFace(N[j][i], N[j + 1][i],
							N[j + 1][i + 1]));
					mesh.addFace(new FlatFace(N[j][i], N[j][i + 1],
							N[j + 1][i + 1]));
				}
		else if (diagType == DiagType.DIAG_TYPE_BACKWARD)
			for (int i = 0; i < ny - 1; i++)
				for (int j = 0; j < nx - 1; j++) {
					mesh.addFace(new FlatFace(N[j][i], N[j + 1][i], N[j][i + 1]));
					mesh.addFace(new FlatFace(N[j + 1][i], N[j][i + 1],
							N[j + 1][i + 1]));
				}
		return N;
	}

	private static Node[][] createRegularMesh(Node[] base, double[] gx,
			double[] gy, Mesh mesh, DiagType diagType) {
		int ny = gx.length;
		int nx = gy.length;
		Node[][] N = new Node[nx][ny];
		for (int i = 0; i < ny; i++) {
			N[0][i] = base[i];
			for (int j = 1; j < nx; j++)
				N[j][i] = mesh.addNode(new Node(gx[i], gy[j]));
		}
		if (diagType == DiagType.DIAG_TYPE_FORWARD)
			for (int i = 0; i < ny - 1; i++)
				for (int j = 0; j < nx - 1; j++) {
					mesh.addFace(new FlatFace(N[j][i], N[j + 1][i],
							N[j + 1][i + 1]));
					mesh.addFace(new FlatFace(N[j][i], N[j][i + 1],
							N[j + 1][i + 1]));
				}
		else if (diagType == DiagType.DIAG_TYPE_BACKWARD)
			for (int i = 0; i < ny - 1; i++)
				for (int j = 0; j < nx - 1; j++) {
					mesh.addFace(new FlatFace(N[j][i], N[j + 1][i], N[j][i + 1]));
					mesh.addFace(new FlatFace(N[j + 1][i], N[j][i + 1],
							N[j + 1][i + 1]));
				}
		return N;
	}

	public static Node[][] createDistortedMesh(Node[] B, double dy, int nx,
			Mesh mesh) {
		double[] bx;
		int ny = B.length;
		bx = new double[ny];
		for (int i = 0; i < ny; i++)
			bx[i] = B[i].getPosition().getX1();

		Node[][] N = new Node[1 + 2 * nx][];
		N[0] = B;
		double y0 = B[0].getPosition().getX2();
		for (int k = 0; k < nx; k++) {
			N[1 + 2 * k] = new Node[ny + 1];
			N[2 + 2 * k] = new Node[ny];

			N[1 + 2 * k][0] = mesh.addNode(new Node(B[0].getPosition().getX1(),
					y0 + (1 + 2 * k) * dy));
			for (int j = 0; j < ny - 1; j++) {
				N[1 + 2 * k][j + 1] = mesh.addNode(new Node((B[j].getPosition()
						.getX1() + B[j + 1].getPosition().getX1()) / 2, y0
						+ (1 + 2 * k) * dy));
				N[2 + 2 * k][j] = mesh.addNode(new Node(B[j].getPosition()
						.getX1(), y0 + (2 + 2 * k) * dy));
			}
			N[1 + 2 * k][ny] = mesh.addNode(new Node(B[ny - 1].getPosition()
					.getX1(), y0 + (1 + 2 * k) * dy));
			N[2 + 2 * k][ny - 1] = mesh.addNode(new Node(B[ny - 1]
					.getPosition().getX1(), y0 + (2 + 2 * k) * dy));

			for (int j = 0; j < ny - 1; j++) {
				mesh.addFace(new FlatFace(N[2 * k][j], N[1 + 2 * k][j],
						N[1 + 2 * k][j + 1]));
				mesh.addFace(new FlatFace(N[2 * k][j], N[1 + 2 * k][j + 1],
						N[2 * k][j + 1]));
				mesh.addFace(new FlatFace(N[1 + 2 * k][j], N[2 + 2 * k][j],
						N[1 + 2 * k][j + 1]));
				mesh.addFace(new FlatFace(N[1 + 2 * k][j + 1], N[2 + 2 * k][j],
						N[2 + 2 * k][j + 1]));
			}
			mesh.addFace(new FlatFace(N[2 * k][ny - 1], N[1 + 2 * k][ny],
					N[1 + 2 * k][ny - 1]));
			mesh.addFace(new FlatFace(N[1 + 2 * k][ny - 1], N[1 + 2 * k][ny],
					N[2 + 2 * k][ny - 1]));

		}
		return N;
	}

	// public static ArrayList<EmbededCrackElement> findElement(
	// CrackingStructure struct, Node... nodes) {
	// ArrayList<EmbededCrackElement> E = new ArrayList<EmbededCrackElement>();
	// int cnt = 0;
	// EmbededCrackElement e;
	// Set<Integer> idSet = new HashSet<Integer>();
	// idSet.add(struct.getNode(0).getId());
	// idSet.add(struct.getNode(1).getId());
	// idSet.add(struct.getNode(2).getId());
	// do {
	// e = struct.getElement(cnt++);
	// for (Node n : nodes) {
	// if (idSet.contains(n.getId()))
	// break;
	// E.add(e);
	// if (nodes.length == 3)
	// break;
	// }
	//
	// } while (cnt < struct.getNumberOfElements());
	// return E;
	// }

	public static ArrayList<Integer> findElement(Structure struct,
			Node... nodes) {
		ArrayList<Integer> E = new ArrayList<Integer>();
		int cnt = 0;
		Set<Integer> idSet = new HashSet<Integer>();
		IElement e;
		for (Node n : nodes)
			idSet.add(n.getId());
		do {
			e = struct.getElements(cnt++);
			Set<Integer> idE = new HashSet<Integer>();
			for (Node n : e.getNodes())
				idE.add(n.getId());
			if (idE.containsAll(idSet))
				E.add(e.getId());

		} while (cnt < struct.getNumberOfElements());
		return E;
	}

}
